package za.co.chaotik.tron.maps;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import za.co.chaotik.tron.AbstractMap;
import za.co.chaotik.tron.Cycle;
import za.co.chaotik.tron.Position;
import za.co.chaotik.tron.exceptions.UnsupportedNumberOfPlayersException;

public class SphericalMap extends AbstractMap {
	public SphericalMap(int width, int height, int numPlayers) {
		super(width, height, numPlayers);
		
		// Create map
		this.positions = new ArrayList<List<Position>>(getHeight());
		for (int rows = 0; rows < getHeight(); rows++) {
			ArrayList<Position> row = new ArrayList<Position>(getWidth());
			this.positions.add(row);
			
			for (int cols = 0; cols < getWidth(); cols++) {
				row.add(new Position(cols, rows));
			}
		}
	}

	@Override
	public void init() throws UnsupportedNumberOfPlayersException {
		// Place cycles
		if (players.size() != 2) {
			// Invalid number of players for this type of map
			throw new UnsupportedNumberOfPlayersException("SphericalMap only supports 2 player games.");
		}
		
		// Init RNG
		Random rng = new Random(new Date().getTime());
		
		// TODO: Randomize starting player
		
		// Randomize starting positions
		int x = rng.nextInt(this.getWidth());
		int y = 1 + rng.nextInt(this.getHeight() - 2); // Exclude the poles
		
		this.addCycle(cycles.get(0), getPosition(x, y));
		
		x = (x + this.getWidth() / 2) % this.getWidth();
		
		this.addCycle(cycles.get(1), getPosition(x, y)); 
	}
	
	@Override
	public Position getPosition(int x, int y) {
		// Make movement wrap around the map from east-west and west-east
		if (x == -1) {
			return super.getPosition(this.getWidth() - 1, y);
		} else if (x == this.getWidth()) {
			return super.getPosition(0, y);
		} else {
			// Handle the poles
			if ((y == 0) || (y == this.getHeight() - 1)) {
				return super.getPosition(this.getWidth() / 2, y);
			} else {
				return super.getPosition(x, y);
			}
		}
	}
	
	@Override
	public List<Position> getValidMoves(Cycle cycle) {
		List<Position> positions = new ArrayList<Position>();
		List<Position> validMoves = new ArrayList<Position>();

		int x = cycle.getX();
		int y = cycle.getY();
		
		if (y == 0) { // at the north pole
			for (int i = 0; i < this.getWidth(); i++) {
				Position pos = this.getPosition(i, 1);
				if (isValidFor(pos, cycle)) {
					positions.add(pos);
				}
			}
		} else if (y == this.getHeight() - 1) { // at the south pole
			for (int i = 0; i < this.getWidth(); i++) {
				Position pos = this.getPosition(i, this.getHeight() - 2);
				if (isValidFor(pos, cycle)) {
					positions.add(pos);
				}
			}
		}
		
		positions.add(this.getPosition(x - 1, y));
		positions.add(this.getPosition(x + 1, y));
		positions.add(this.getPosition(x, y - 1));
		positions.add(this.getPosition(x, y + 1));
		
		for (Position pos : positions) {
			if (isValidFor(pos, cycle)) {
				validMoves.add(pos);
			}
		}
		
		return validMoves;
	}
	
	private boolean isValidFor(Position pos, Cycle cycle) {
		// Position must be on the map
		if (pos == null) {
			return false;
		}
		
		// Cycle may not move backwards
		List<Position> cycleMoves = moves.get(cycle.getOwner());
		
		if ((cycleMoves.size() > 1) && (cycleMoves.get(cycleMoves.size() - 2).equals(pos))) {
			return false;
		}
		
		return true;
	}	
}
